Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development


How the manager code runs on the server

This example shows one way the managers run code without binding the AppServer session, by packaging that code as individually callable external procedures. There is another technique as well that you see in the manager code that also merits explaining.

Sometimes the code called from the client cannot effectively be packaged up into a separate .p file. In fact, a procedure on the client sometimes really must call itself on the server in order to return needed information. In other words, the client session must be able to run a procedure as if it were implemented locally. If the code for the procedure cannot be local, then the version of that procedure on the client must run a different version of itself on the server,where the code resides, that, among other things , accesses the database.

In this case, the server code is embedded inside an internal procedure that is part of the client-side manager. However, the server-specific code is compiled out of the client manager. So the entry point name exists on both sides of the server connection, but the code inside is different.

Let’s look an example from the General Manager this time, in the source file af/app/rygenmgrp.i. The procedure getEntityDescription is a very useful general-purpose routine that can return the value of any field in the database, given the mnemonic or dump name of the table, the name of the field, and the unique object ID key value for the record within the table.

Because getEntityDescription can return the value of any field, there is no practical way for the client to cache all the data it might need to look at. For this reason, if code runs the procedure on the client, the client code turns around and runs the same procedure name on the server. So, first the procedure defines the parameters common to both client and server code, as shown:

PROCEDURE getEntityDescription: 
DEFINE INPUT  PARAMETER pcEntityMnemonic        AS CHARACTER  NO-UNDO. 
DEFINE INPUT  PARAMETER pdEntityObj             AS DECIMAL    NO-UNDO. 
DEFINE INPUT  PARAMETER pcFieldName             AS CHARACTER  NO-UNDO. 
DEFINE OUTPUT PARAMETER pcEntityDescriptor      AS CHARACTER  NO-UNDO. 

Then it uses the server-side preprocessor to compile in a call to the server if server-side is not defined. The include file dynlaunch.i that drives the call is the most efficient way to make a call to an internal procedure inside a server-side program, and return values from that procedure. Its named include file arguments include the name of the procedure or manager to run or access on the server; the name of the internal procedure to run; and a set of three arguments for each parameter to the internal procedure that specify the parameter’s INPUT, OUTPUT, or INPUT-OUTPUT mode, the parameter name, and its data type. The dynlaunch.i include file turns this into a dynamic reference to the internal procedure that does all its work with a single AppServer hit, as shown:

&IF DEFINED(server-side) = 0 &THEN 
    { 
     dynlaunch.i &PLIP              = "'GeneralManager'" 
                 &iProc             = "'getEntityDescription'" 
                 &compileStaticCall = NO                  
                 &mode1 = INPUT  &parm1 = pcEntityMnemonic    
                                 &dataType1 = CHARACTER 
                 &mode2 = INPUT  &parm2 = pdEntityObj         
                                 &dataType2 = DECIMAL 
                 &mode3 = INPUT  &parm3 = pcFieldName         
                                 &dataType3 = CHARACTER 
                 &mode4 = OUTPUT &parm4 = pcEntityDescriptor  
                                 &dataType4 = CHARACTER 
    } 
    IF ERROR-STATUS:ERROR OR RETURN-VALUE <> "":U THEN RETURN ERROR 
RETURN-VALUE. 

This is different from the previous example, where the server code is run as a separate procedure when needed and then goes away. In this case, the procedure you want to run is inside the server-side General Manager that is already up and running, so you do not want to run a new copy of it. The dynlaunch.i file works whether the server-side program is already running as a persistent procedure or manager, or whether it must be started for the request and then terminated when it returns.

This is the end of the code that gets run if the procedure is compiled for the client. Following this is an &ELSE block that is compiled if the code is compiled for the server. This is the guts of getEntityDescription, the code that does all the work:

&ELSE 
    DEFINE VARIABLE cQueryPrepareString             AS CHARACTER    NO-UNDO. 
    DEFINE VARIABLE cTableObjectFieldName           AS CHARACTER    NO-UNDO. 
    DEFINE VARIABLE cTableBase                      AS CHARACTER    NO-UNDO. 
    DEFINE VARIABLE hQuery                          AS HANDLE       NO-UNDO. 
    DEFINE VARIABLE hBuffer                         AS HANDLE       NO-UNDO. 
    DEFINE VARIABLE hDescriptionField               AS HANDLE       NO-UNDO. 
    DEFINE VARIABLE hCurrentField                   AS HANDLE       NO-UNDO. 
    DEFINE VARIABLE iFieldLoop                      AS INTEGER      NO-UNDO. 
    FIND ttEntityMnemonic WHERE 
         ttEntityMnemonic.entity_mnemonic = pcEntityMnemonic 
         NO-ERROR. 
…etc. 

In this way, code in a single file (the common code include file in this case) can be compiled two different ways to execute in a coordinated fashion between client and server.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095